linux 键盘映射xremap
·
Table of Contents
xremap
- xremap is a key remapper for Linux. Unlike xmodmap, it supports app-specific remapping and Wayland.
- xremap 和 evdev 都是 Linux 下的键盘重映射工具,主要区别在于 xremap 专注于应用层,提供更灵活的组合键和上下文感知(如窗口特定、虚拟修饰键)功能,而 evdev(底层驱动)则更侧重硬件/驱动层面的低级事件捕获与重映射,xremap 可以基于 evdev 实现高级功能,两者常结合使用,xremap 更易用,evdev 性能更低延迟。
cargo install xremap --features gnome # GNOME Wayland
查看当前 uinput 设备(需要 root)
xremap 使用 /dev/uinput 创建虚拟输入设备。异常退出可能导致设备未释放。
sudo cat /sys/class/misc/uinput/dev
# 查看 uinput 使用者(粗略判断是否被占用)
lsof /dev/uinput
# 或
fuser /dev/uinput
Running xremap without sudo
To do so, your normal user should be able to use evdev and uinput without sudo. In Ubuntu, this can be configured by running the following commands and rebooting your machine.
sudo gpasswd -a YOUR_USER input
echo 'KERNEL=="uinput", GROUP="input", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/input.rules
lsmod | grep uinput
# If it shows up empty:
echo uinput | sudo tee /etc/modules-load.d/uinput.conf
Reboot the machine afterwards or try:
sudo modprobe uinput
sudo udevadm control --reload-rules && sudo udevadm trigger
GNOME Wayland
- Install xremap's GNOME Shell extension from this link, switching OFF to ON.
- If you use sudo to run xremap, also click here.
- Update /usr/share/dbus-1/session.conf as follows, and reboot your machine.
<policy context="default">
+ <allow user="root"/>
<!-- Allow everything to be sent -->
<allow send_destination="*" eavesdrop="true"/>
<!-- Allow everything to be received -->
example
This allows a key to be held indefinitely without triggering its held state, which is ideal for keys that also serve as modifiers. For example, you can make the Space key act as Shift when held and combined with another key, but still type a regular Space when tapped.
modmap:
- name: Space as Shift
remap:
Space:
held: Shift_L
alone: Space
free_hold: true # Optional, defaults to false.
问题排查
# 普通用户执行要求
sudo usermod -aG input $USER
# 创建 udev 规则: 新建文件 /etc/udev/rules.d/99-input.rules,内容如下:
KERNEL=="uinput", GROUP="input", MODE="0660"
sudo udevadm control --reload-rules && sudo udevadm trigger
# 查找你的键盘设备 寻找 Name="...Keyboard..." 的条目,记下它的 Handlers(例如 event3)
cat /proc/bus/input/devices
/home/peter/.cargo/bin/xremap /home/peter/project/tools/dotfiles/keymap.yml --device /dev/input/event3
错误举例
- 如果 xremap 以错误权限启动并崩溃,它可能已经打开了虚拟设备但没有正常关闭,导致 GNOME 认为有一个“死键”一直被按下。
xremap 会创建一个名为 xremap 的虚拟键盘。如果程序崩溃了但设备还在,GNOME 会继续尝试从这个“僵尸”设备读取输入
lsinput
# 或者使用更通用的命令
grep -E 'Name|Handlers' /proc/bus/input/devices
- 寻找关键字: 在输出中找名称包含 "xremap" 的设备。
- 判断异常: 如果 xremap 进程已经杀掉了,但 xremap 虚拟设备依然出现在列表中,说明设备未正常卸载,这通常就是导致 GNOME 异常的元凶。
使用 libinput 监控实时事件
- 你可以实时观察到底是哪个设备在“发疯”(比如不停发送 Ctrl 信号)。
sudo libinput debug-events
- 观察输出: * 如果你没有按键,但屏幕滚动显示 KEY_LEFTCTRL 或 KEY_RIGHTALT 的 pressed 事件,说明存在“死键”。
- 看输出条目左侧的设备名称。如果是来自 xremap 设备,说明是驱动层模拟出的问题;如果是来自你的物理键盘,说明物理设备可能被锁死在了按下状态
排查设备“锁死”(EVIOCGRAB)
- xremap 运行时会“抓取”物理键盘。如果它崩溃时没有释放抓取权限,物理键盘的信号就无法到达 GNOME,导致你感觉键盘“失灵”。
ps aux | grep xremap
sudo killall -9 xremap
通常进程结束后,内核会自动释放设备抓取。
快速恢复手段
- 如果你正处于“环境异常”(键盘乱跳或没反应)中,尝试以下组合拳:
- 拔插键盘: 如果是外接键盘,拔掉重插可以强制重置内核对该设备的输入状态。
- 强制销毁虚拟设备: 如果 xremap 已经退出但设备还在,尝试移除 uinput 模块(慎用,仅在没反应时尝试):
sudo modprobe -r uinput && sudo modprobe uinput
- 重启 GNOME Shell:
- X11 环境: 按 Alt + F2,输入 r 然后回车。
- Wayland 环境: 只能通过登出(Log out)并重新登录来重置输入状态。
终极手段:重置 uinput 子系统
# 卸载并重载 uinput 模块(需 root,会断开所有虚拟输入设备)
sudo rmmod uinput
sudo modprobe uinput
# 此操作会使所有依赖 uinput 的程序(如 xremap、keyd、interception-tools)失效,需重启它们
强制释放所有“卡住”的修饰键(最常用、最有效)
在 GNOME/X11 下,模拟释放所有修饰键即可快速恢复:
# 方法 1:使用 xdotool(推荐)
xdotool keyup Shift_L Shift_R Control_L Control_R Alt_L Alt_R Super_L Super_R
# 方法 2:使用 xte(来自 xautomation 包)
xte 'keyup Shift_L' 'keyup Control_L' 'keyup Alt_L' 'keyup Super_L'
# 方法 3:重启输入法/IM 框架(有时 IBus/Rime 会缓存按键状态)
ibus restart
在 GNOME Wayland 会话中,xdotool 可能失效(因非 X11 后端),此时:
Ctrl+Alt+F3 → 登录 → Ctrl+Alt+F1(或 F2)回到 GNOME
# 这会强制重置输入栈。
# 重启 GNOME Shell(不注销):
# 在 GNOME 中按 Alt+F2,输入:
r
# 或终端执行
busctl --user call org.gnome.Shell /org/gnome/Shell org.gnome.Shell Eval s 'global.reexec_self()'
事件监听
sudo apt install evtest
# 列出所有 X 输入设备
xinput list
# 查找类似 “xremap virtual keyboard” 的设备
xinput list --short | grep -i remap
# 若找到,检查其按键状态(需 evtest)
sudo evtest
# → 选择对应设备编号,观察是否有持续的 KEY_XXX (code xxx) DOWN 事件
# xremap 默认创建一个 uinput virtual device,名字可能为 xremap virtual keyboard 或类似。
xremap 的实现原理
xremap 的工作流程可以分为:拦截 (Grab) -> 转换 (Remap) -> 重发 (Emit) 。
1. 拦截层 (evdev + EVIOCGRAB)
- 读取: Linux 内核通过
/dev/input/event*暴露出原始的输入事件。xremap启动后,会打开你的物理键盘设备文件。 - 独占锁: 这是关键。它会对物理键盘调用
ioctl(fd, EVIOCGRAB, 1)。这会告诉内核:“除了我,谁也不要把这个键盘的事件发给别人”。 - 结果: 此时你按下键盘,GNOME 或 X11 是接收不到任何信号的,信号全被
xremap截获了。
2. 逻辑转换层 (Logic)
xremap内部维护一个状态机。当你按下 $Alt\_R$ 时,它不会立即动作,而是根据你的配置文件(如application过滤)检查当前活跃窗口。- 在 Wayland 下,它通过特定的合成器协议或 GNOME 扩展获取窗口信息;在 X11 下,它通过 Xlib 获取
_NET_ACTIVE_WINDOW。
3. 重发层 (uinput)
- 虚拟设备:
xremap调用/dev/uinput模块,在系统中注册一个全新的虚拟键盘(通常名字就叫xremap)。 - 转换输出: 当它拦截到 $Alt\_R$ 并判断需要映射时,它会向
uinput设备写入 $Control\_L$ 的事件序列。 - 接收: GNOME 看到的是来自这个“虚拟键盘”的 $Control\_L$ 信号,从而执行相应的操作
类似工具
- https://github.com/jtroo/kanata
- https://github.com/kmonad/kmonad
loadkeys(console keymap)
- 这是 Linux 控制台专用 的键盘映射。
- dumpkeys > keymap.map
# 编辑 keymap.map
sudo loadkeys keymap.map
- 直接作用在 VT
- 内核级
- 稳定
- 不支持复杂逻辑
kbd / console-setup
用于
- 服务器
- rescue
- initramfs
sudo dpkg-reconfigure keyboard-configuration
其他类似项目
| 项目 | 架构 | Wayland | 特点 |
|---|---|---|---|
| xremap | evdev + uinput | ⚠️ | YAML,灵活 |
| keyd | evdev + uinput | ✅ | 稳定、简单 |
| kmonad | evdev + uinput | ⚠️ | Lisp DSL |
| interception-tools | evdev | ⚠️ | 管道式 |
| setxkbmap | XKB | ✅ | 最稳 |
| xmodmap | X11 | ❌ | 已过时 |
| Karabiner (macOS) | HID | ✅ | 架构标杆 |
- 路线 A:evdev + uinput(xremap 路线)
- 做系统级 remap
- 不依赖 Wayland 应用识别
- 能接受 input 权限
- 路线 B:XKB 层(最稳)
- 不抓设备
- 改的是 键位语义
- compositor 原生支持
- 路线 C:Wayland Compositor 插件(最干净)
- 在 compositor 内部改键